package org.smile.josql.parse;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;

import org.smile.exception.JoSQLParseException;

public class WhereTokener {
	
	private static char[] OPRATER={'<','>','!','='};
	private int character;
	private boolean eof;
	private int index;
	private int line;
	private char previous;
	private final Reader reader;
	private boolean usePrevious;

	
	public WhereTokener(Reader reader) {
		this.reader = reader.markSupported() ? reader : new BufferedReader(
				reader);
		this.eof = false;
		this.usePrevious = false;
		this.previous = 0;
		this.index = 0;
		this.character = 1;
		this.line = 1;
	}

	
	public WhereTokener(InputStream inputStream) throws JoSQLParseException {
		this(new InputStreamReader(inputStream));
	}

	
	public WhereTokener(String s) {
		this(new StringReader(s));
	}

	
	public void back() throws JoSQLParseException {
		if (this.usePrevious || this.index <= 0) {
			throw new JoSQLParseException("Stepping back two steps is not supported");
		}
		this.index -= 1;
		this.character -= 1;
		this.usePrevious = true;
		this.eof = false;
	}

	

	public boolean end() {
		return this.eof && !this.usePrevious;
	}

	/**
	 * 判断是否还有更多字符
	 * @return
	 * @throws JoSQLParseException
	 */
	public boolean more() throws JoSQLParseException {
		this.next();
		if (this.end()) {
			return false;
		}
		this.back();
		return true;
	}

	
	public char next() throws JoSQLParseException {
		int c;
		if (this.usePrevious) {
			this.usePrevious = false;
			c = this.previous;
		} else {
			try {
				c = this.reader.read();
			} catch (IOException exception) {
				throw new JoSQLParseException(exception);
			}

			if (c <= 0) { 
				this.eof = true;
				c = 0;
			}
		}
		this.index += 1;
		if (this.previous == '\r') {
			this.line += 1;
			this.character = c == '\n' ? 0 : 1;
		} else if (c == '\n') {
			this.line += 1;
			this.character = 0;
		} else {
			this.character += 1;
		}
		this.previous = (char) c;
		return this.previous;
	}

	
	public char next(char c) throws JoSQLParseException {
		char n = this.next();
		if (n != c) {
			throw this.parseError("Expected '" + c + "' and instead saw '" + n
					+ "'");
		}
		return n;
	}

	public String next(int n) throws JoSQLParseException {
		if (n == 0) {
			return "";
		}

		char[] chars = new char[n];
		int pos = 0;

		while (pos < n) {
			chars[pos] = this.next();
			if (this.end()) {
				throw this.parseError("Substring bounds error");
			}
			pos += 1;
		}
		return new String(chars);
	}

	
	public char nextClean() throws JoSQLParseException {
		for (;;) {
			char c = this.next();
			if (c == 0 || c > ' ') {
				return c;
			}
		}
	}

	
	public String nextString(char quote) throws JoSQLParseException {
		char c;
		StringBuffer sb = new StringBuffer();
		for (;;) {
			c = this.next();
			switch (c) {
			case 0:
			case '\n':
			case '\r':
				throw this.parseError("Unterminated string");
			case '\\':
				c = this.next();
				switch (c) {
				case 'b':
					sb.append('\b');
					break;
				case 't':
					sb.append('\t');
					break;
				case 'n':
					sb.append('\n');
					break;
				case 'f':
					sb.append('\f');
					break;
				case 'r':
					sb.append('\r');
					break;
				case 'u':
					sb.append((char) Integer.parseInt(this.next(4), 16));
					break;
				case '"':
				case '\'':
				case '\\':
				case '/':
					sb.append(c);
					break;
				default:
					throw this.parseError("Illegal escape.");
				}
				break;
			default:
				if (c == quote) {
					return sb.toString();
				}
				sb.append(c);
			}
		}
	}

	
	public String nextTo(char delimiter) throws JoSQLParseException {
		StringBuffer sb = new StringBuffer();
		for (;;) {
			char c = this.next();
			if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
				if (c != 0) {
					this.back();
				}
				return sb.toString().trim();
			}
			sb.append(c);
		}
	}

	
	public String nextTo(String delimiters) throws JoSQLParseException {
		char c;
		StringBuffer sb = new StringBuffer();
		for (;;) {
			c = this.next();
			if (delimiters.indexOf(c) >= 0 || c == 0 || c == '\n' || c == '\r') {
				if (c != 0) {
					this.back();
				}
				return sb.toString().trim();
			}
			sb.append(c);
		}
	}
	public String nextString() throws JoSQLParseException{
		StringBuffer sb=new StringBuffer();
		char temp=0;
		char pro=0;
		char c;
		for(;;){
			c=this.next();
			switch (c) {
			case 0:
			case '\n':
			case '\r':
				return sb.toString();
			case '\\':
				throw this.parseError("Unterminated string：\\");
			case '\'':
				if(pro==0){
					temp=c;
				}else{
					char next=this.next();
					if(!(next==' '||next=='\n'||next=='\r'||next==0)){
						this.back();
						sb.append(c);
					}else{
						this.skipSpace();
						return sb.toString();
					}
				}
				break;
			default:
				if (c==' '){
					if(temp!=0){
						sb.append(c);
					}else{
						this.skipSpace();
						return sb.toString();
					}
				}else if(this.isOperator(c)) {
					this.back();
					return sb.toString();
				}
				sb.append(c);
				//保存上一个为当前字符
				pro=c;
			}
		}
	}
	
	public String nextOperator() throws JoSQLParseException{
		StringBuffer sb=new StringBuffer();
		char c;
		while(true){
			c=this.next();
			if(this.isOperator(c)){
				sb.append(c);
				for(;;){
					c=this.next();
					if(this.isOperator(c)){
						sb.append(c);
					}else{
						if(c==' '){
							this.skipSpace();
						}else{
							this.back();
						}
						return sb.toString();
					}
				}
			}else{
				this.back();
				while(true){
					String str= this.nextKeyWord();
					if("like".equalsIgnoreCase(str)){
						return "LIKE";
					}else{
						sb.append(str);
						for(int i=0;i<2;i++){
							str=this.nextKeyWord();
							sb.append(" ").append(str);
							if("null".equalsIgnoreCase(str)){
								return sb.toString();
							}
						}
						throw parseError("语法错误");
					}
				}
			}
		}
	}
	/**
	 * 下一个关键字
	 * @return
	 * @throws JoSQLParseException
	 */
	public String nextKeyWord() throws JoSQLParseException{
		char c;
		StringBuffer key=new StringBuffer();
		c=this.next();
		if(c==0){
			return key.toString();
		}
		key.append(c);
		for(;;){
			while((c=this.next())!=' '&&c>0){
				key.append(c);
			}
			this.skipSpace();
			if("like".equalsIgnoreCase(key.toString())){
				return "LIKE";
			}
			else if("is".equalsIgnoreCase(key.toString())){
				return "IS";
			}
			else if("not".equalsIgnoreCase(key.toString())){
				return "NOT";
			}
			else if("and".equalsIgnoreCase(key.toString())){
				return "AND";
			}
			else if("or".equalsIgnoreCase(key.toString())){
				return "OR";
			}else if("null".equalsIgnoreCase(key.toString())){
				return "NULL";
			}else{
				throw parseError("not supper keyword on where substring :"+key.toString());
			}
		}
	}
	/**
	 * 跳过空格
	 * @throws JoSQLParseException 
	 */
	public void skipSpace() throws JoSQLParseException{
		char c;
		while(true){
			if(!((c=this.next())==' ')){
				this.back();
				break;
			}
		}
	}
	/**
	 * 是不是操作符
	 * @param c
	 * @return
	 */
	public boolean isOperator(char c){
		for(int i=0;i<OPRATER.length;i++){
			if(c==OPRATER[i]){
				return true;
			}
		}
		return false;
	}
	
	
	public char skipTo(char to) throws JoSQLParseException {
		char c;
		try {
			int startIndex = this.index;
			int startCharacter = this.character;
			int startLine = this.line;
			this.reader.mark(Integer.MAX_VALUE);
			do {
				c = this.next();
				if (c == 0) {
					this.reader.reset();
					this.index = startIndex;
					this.character = startCharacter;
					this.line = startLine;
					return c;
				}
			} while (c != to);
		} catch (IOException exc) {
			throw new JoSQLParseException(exc);
		}

		this.back();
		return c;
	}

	
	public JoSQLParseException parseError(String message) {
		return new JoSQLParseException(message + this.toString());
	}

	
	public String toString() {
		return " at " + this.index + " [character " + this.character + " line "
				+ this.line + "]";
	}
}
